package com.by.log.handler;

import com.by.log.Logger;
import com.by.log.TraceContext;
import com.by.log.constants.LogLevel;
import com.by.log.entity.LogEntry;
import com.by.log.statistic.DefaultStatisticCollector;
import com.by.log.TraceContextUtil;

import java.io.*;
import java.time.format.DateTimeFormatter;

/**
 * 默认日志实现类
 * 支持终端和文件两种输出方式
 */
public class DefaultLogger implements Logger {
    private final String className;
    private final LogLevel level;
    private final String logFilePath;
    private LogStorage logStorage;
    private final DefaultStatisticCollector statisticCollector;
    
    public DefaultLogger(String className) {
        this(className, LogLevel.INFO, null);
    }
    
    public DefaultLogger(String className, LogLevel level, String logFilePath) {
        this.className = className;
        this.level = level;
        this.logFilePath = logFilePath;
        this.statisticCollector = new DefaultStatisticCollector(className);
    }

    @Override
    public void trace(String format, Object... args) {
        if (level.isGreaterOrEqual(LogLevel.TRACE)) {
            log(LogLevel.TRACE, format, args);
        }
    }

    @Override
    public void debug(String format, Object... args) {
        if (level.isGreaterOrEqual(LogLevel.DEBUG)) {
            log(LogLevel.DEBUG, format, args);
        }
    }

    @Override
    public void info(String format, Object... args) {
        if (level.isGreaterOrEqual(LogLevel.INFO)) {
            log(LogLevel.INFO, format, args);
        }
    }

    @Override
    public void warn(String format, Object... args) {
        if (level.isGreaterOrEqual(LogLevel.WARN)) {
            log(LogLevel.WARN, format, args);
        }
    }

    @Override
    public void error(String format, Object... args) {
        if (level.isGreaterOrEqual(LogLevel.ERROR)) {
            log(LogLevel.ERROR, format, args);
        }
    }

    @Override
    public void error(Throwable throwable, String format, Object... args) {
        if (level.isGreaterOrEqual(LogLevel.ERROR)) {
            log(LogLevel.ERROR, format, args);
            if (throwable != null) {
                logThrowable(throwable);
            }
        }
    }

    @Override
    public void info(LogEntry context, String format, Object... args) {
        if (level.isGreaterOrEqual(LogLevel.INFO)) {
            logWithContext(LogLevel.INFO, context, format, args);
        }
    }

    @Override
    public void error(LogEntry context, Throwable throwable, String format, Object... args) {
        if (level.isGreaterOrEqual(LogLevel.ERROR)) {
            logWithContext(LogLevel.ERROR, context, format, args);
            if (throwable != null) {
                logThrowable(throwable);
            }
        }
    }

    @Override
    public void recordMethodCall(boolean success, long responseTime) {
        // 记录统计信息
        statisticCollector.recordCall(success, responseTime);
        
        LogEntry entry = new LogEntry();
        entry.setLevel(LogLevel.INFO);
        entry.setClassName(className);
        entry.setMethodName(getCallerMethodName());
        entry.setSuccess(success);
        entry.setExecutionTime(responseTime);
        entry.setMessage(String.format("Method %s completed with %s in %d ms", 
            entry.getMethodName(), success ? "success" : "failure", responseTime));
        
        // 添加追踪信息
        TraceContextUtil.TraceData traceData = TraceContextUtil.getCurrentTraceData();
        if (traceData != null) {
            entry.setTraceId(traceData.getTraceId());
            entry.setSpanId(traceData.getSpanId());
            entry.setParentSpanId(traceData.getParentSpanId());
            entry.setServiceName(traceData.getServiceName());
            entry.setMethodName(traceData.getMethodName());
        }
        
        doLog(entry);
    }
    
    @Override
    public void recordRouteCall(String route, boolean success, long responseTime) {
        // 记录路由统计信息
        statisticCollector.recordDimensionalCall("route", route, success, responseTime);
        
        LogEntry entry = new LogEntry();
        entry.setLevel(LogLevel.INFO);
        entry.setClassName(className);
        entry.setMethodName(getCallerMethodName());
        entry.setSuccess(success);
        entry.setExecutionTime(responseTime);
        entry.setMessage(String.format("Route %s completed with %s in %d ms", 
            route, success ? "success" : "failure", responseTime));
        
        // 添加追踪信息
        TraceContextUtil.TraceData traceData = TraceContextUtil.getCurrentTraceData();
        if (traceData != null) {
            entry.setTraceId(traceData.getTraceId());
            entry.setSpanId(traceData.getSpanId());
            entry.setParentSpanId(traceData.getParentSpanId());
            entry.setServiceName(traceData.getServiceName());
            entry.setMethodName(traceData.getMethodName());
        }
        
        doLog(entry);
    }
    
    @Override
    public void recordDimensionalCall(String dimension, String targetId, boolean success, long responseTime) {
        // 记录维度统计信息
        statisticCollector.recordDimensionalCall(dimension, targetId, success, responseTime);
        
        LogEntry entry = new LogEntry();
        entry.setLevel(LogLevel.INFO);
        entry.setClassName(className);
        entry.setMethodName(getCallerMethodName());
        entry.setSuccess(success);
        entry.setExecutionTime(responseTime);
        entry.setMessage(String.format("Dimension %s:%s completed with %s in %d ms", 
            dimension, targetId, success ? "success" : "failure", responseTime));
        
        // 添加追踪信息
        TraceContextUtil.TraceData traceData = TraceContextUtil.getCurrentTraceData();
        if (traceData != null) {
            entry.setTraceId(traceData.getTraceId());
            entry.setSpanId(traceData.getSpanId());
            entry.setParentSpanId(traceData.getParentSpanId());
            entry.setServiceName(traceData.getServiceName());
            entry.setMethodName(traceData.getMethodName());
        }
        
        doLog(entry);
    }

    @Override
    public void trace(String traceId, String spanId, String parentSpanId, String message) {

    }

    @Override
    public void logWithTrace(String level, String traceId, String spanId, String parentSpanId, String message, Object... args) {

    }

    @Override
    public void setLogStorage(LogStorage storage) {
        this.logStorage = storage;
    }

    private void log(LogLevel level, String format, Object... args) {
        LogEntry entry = new LogEntry();
        entry.setLevel(level);
        entry.setClassName(className);
        entry.setMessage(formatMessage(format, args));
        
        // 添加追踪信息
        TraceContextUtil.TraceData traceData = TraceContextUtil.getCurrentTraceData();
        if (traceData != null) {
            entry.setTraceId(traceData.getTraceId());
            entry.setSpanId(traceData.getSpanId());
            entry.setParentSpanId(traceData.getParentSpanId());
            entry.setServiceName(traceData.getServiceName());
            entry.setMethodName(traceData.getMethodName());
        }
        
        doLog(entry);
    }

    private void logWithContext(LogLevel level, LogEntry context, String format, Object... args) {
        LogEntry entry = new LogEntry();
        entry.setLevel(level);
        entry.setClassName(className);
        entry.setMessage(formatMessage(format, args));
        entry.setTraceId(context.getTraceId());
        entry.setSpanId(context.getSpanId());
        entry.setParentSpanId(context.getParentSpanId());
        entry.setThreadName(context.getThreadName());
        entry.setThreadId(context.getThreadId());
        entry.setServiceName(context.getServiceName());
        entry.setMethodName(context.getMethodName());
        entry.setExecutionTime(context.getExecutionTime());
        entry.setSuccess(context.getSuccess());
        entry.setExtendedFields(context.getExtendedFields());
        doLog(entry);
    }

    private void doLog(LogEntry entry) {
        // 如果设置了LogStorage，则使用它来存储日志
        if (logStorage != null) {
            logStorage.storeLog(entry);
            return;
        }
        
        // 否则使用默认的输出方式
        String logMessage = formatLogEntry(entry);
        
        // 输出到终端
        System.out.println(logMessage);
        
        // 如果指定了文件路径，则也输出到文件
        if (logFilePath != null && !logFilePath.isEmpty()) {
            writeToFile(logMessage);
        }
    }

    private String formatLogEntry(LogEntry entry) {
        StringBuilder sb = new StringBuilder();
        sb.append("[")
          .append(entry.getTimestamp().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")))
          .append("] [")
          .append(entry.getLevel())
          .append("] [")
          .append(entry.getThreadName())
          .append("] [")
          .append(entry.getClassName())
          .append("] ");
          
        // 添加追踪信息
        if (entry.getTraceId() != null) {
            sb.append("[TraceId: ").append(entry.getTraceId()).append("] ");
        }
        
        if (entry.getSpanId() != null) {
            sb.append("[SpanId: ").append(entry.getSpanId()).append("] ");
        }
        
        if (entry.getParentSpanId() != null) {
            sb.append("[ParentSpanId: ").append(entry.getParentSpanId()).append("] ");
        }
        
        if (entry.getServiceName() != null) {
            sb.append("[Service: ").append(entry.getServiceName()).append("] ");
        }
        
        if (entry.getMethodName() != null) {
            sb.append("[Method: ").append(entry.getMethodName()).append("] ");
        }
        
        sb.append(entry.getMessage() != null ? entry.getMessage() : "");
          
        if (entry.getExecutionTime() != null) {
            sb.append(" [ExecTime: ").append(entry.getExecutionTime()).append("ms]");
        }
        
        if (entry.getSuccess() != null) {
            sb.append(" [Success: ").append(entry.getSuccess()).append("]");
        }
        
        return sb.toString();
    }

    private String formatMessage(String format, Object... args) {
        if (args == null || args.length == 0) {
            return format;
        }
        
        // 简单实现参数格式化
        String message = format;
        for (Object arg : args) {
            message = message.replaceFirst("\\{\\}", String.valueOf(arg));
        }
        return message;
    }

    private void logThrowable(Throwable throwable) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        throwable.printStackTrace(pw);
        String stackTrace = sw.toString();
        
        // 如果设置了LogStorage，则使用它来存储异常
        if (logStorage != null) {
            LogEntry entry = new LogEntry();
            entry.setLevel(LogLevel.ERROR);
            entry.setClassName(className);
            entry.setThrowable(stackTrace);
            
            // 添加追踪信息
            TraceContext traceContext = TraceContext.getCurrentContext();
            if (traceContext != null) {
                entry.setTraceId(traceContext.getTraceId());
                entry.setSpanId(traceContext.getSpanId());
                entry.setParentSpanId(traceContext.getParentSpanId());
            }
            
            logStorage.storeLog(entry);
            return;
        }
        
        // 否则使用默认的输出方式
        // 输出到终端
        System.err.println(stackTrace);
        
        // 如果指定了文件路径，则也输出到文件
        if (logFilePath != null && !logFilePath.isEmpty()) {
            writeToFile(stackTrace);
        }
    }

    private void writeToFile(String message) {
        try (FileWriter fw = new FileWriter(logFilePath, true);
             BufferedWriter bw = new BufferedWriter(fw);
             PrintWriter out = new PrintWriter(bw)) {
            out.println(message);
        } catch (IOException e) {
            // 如果写入文件失败，至少输出到控制台
            System.err.println("Failed to write log to file: " + e.getMessage());
            System.err.println(message);
        }
    }
    
    private String getCallerMethodName() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        // 查找调用recordMethodCall方法的类和方法
        for (int i = 0; i < stackTrace.length; i++) {
            StackTraceElement element = stackTrace[i];
            if ("recordMethodCall".equals(element.getMethodName()) && 
                DefaultLogger.class.getName().equals(element.getClassName())) {
                // 找到下一个用户调用的方法
                if (i + 2 < stackTrace.length) {
                    return stackTrace[i + 2].getMethodName();
                }
            }
        }
        return "unknown";
    }
    
    /**
     * 获取统计收集器
     * @return 统计收集器
     */
    public DefaultStatisticCollector getStatisticCollector() {
        return statisticCollector;
    }
}